一个叫木头,一个叫马尾

【译】使用React Hook加载图片

译者注:已经很少用React写东西了,但遇到好的文章还是想推荐出来...

文章译自 Eyk Rehbein 所写的Fetch images with a React hook[1]


我们都知道获取和显示图片的默认方式:

<img src="/image.jpg" />

整个行业的框架和库都在使用这种方式。但是,如果你想改善用户体验,在图片还在加载的时候显示loading效果;或者在加载出错时给出提示信息,怎么办?不幸的是,在默认的图片加载方式下,这并不容易实现。

不过不用担心,解决方法是有的。我们可以直接用 React Hook 并行加载图片,它还能告诉你图片何时加载完以及是否出现错误。

The Hook (图片Hook)

首先,我想介绍一下该 Hook:

import { useState, useEffect } from "react";

export const useImage = (src: string) => {
  const [hasLoaded, setHasLoaded] = useState(false);
  const [hasError, setHasError] = useState(false);
  const [hasStartedInitialFetch, setHasStartedInitialFetch] = useState(false);

  useEffect(() => {
    setHasStartedInitialFetch(true);
    setHasLoaded(false);
    setHasError(false);

    // Here's where the magic happens.
    // 奇迹在这里
    const image = new Image();
    image.src = src;

    const handleError = () => {
      setHasError(true);
    };

    const handleLoad = () => {
      setHasLoaded(true);
      setHasError(false);
    };

    image.onerror = handleError;
    image.onload = handleLoad;

    return () => {
      image.removeEventListener("error", handleError);
      image.removeEventListener("load", handleLoad);
    };
  }, [src]);

  return { hasLoaded, hasError, hasStartedInitialFetch };
};

这个 Hook 做了什么呢?好吧,它基本上是把图片加载到缓存中,或者从缓存中取出图片(如果已经加载过)。

同时,它还提供了一些有用的信息,比如图像是否已经加载完成、加载过程中是否发生了错误、或者初次加载是否开始。然后,只要图像还没有加载完成,你就可以利用这些信息在图像的位置上放置一个加载指示。一个使用了该Hook的例子可以像下面这样:

const Demo = () => {
  const imageUrl = "/image.png";

  const { hasLoaded, hasError } = useImage(imageUrl);

  if (hasError) {
    return null;
  }

  return (
    <>
      ...
      {!hasLoaded && <LoadingIndicator />}
      {hasLoaded && <img src={imageUrl} />}
    </>
  );
};

But doesn't the image load twice? (但上例中图片会不会被加载2次?)

并非如此,尽管人们预期会这样,毕竟上例的 Hookimg 都引用了该图片源(imageUrl)。然而,无论图片源的引用频率有多高,都只会发送一个请求。这带来的好处是,该图片源无论在页面的哪里引用,hasLoaded 总是会变成 true(假设加载是成功的)。

Conclusion (总结)

如果你想以一种简单的方式加载图像,同时又能知道确切的加载状态,我强烈推荐该 Hook。事实上,typeafe.blog上的所有文章缩略图都是用这个Hook加载的。如果你网络不好,你可以看到一个加载提示(loading indicator),直到图像加载完成。

[1]

Fetch images with a React hook: https://typesafe.blog/article/fetch-images-with-a-react-hook